package com.na.game.graphics;

import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Matrix;
import android.graphics.NinePatch;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Paint.FontMetricsInt;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.util.SparseArray;

import com.na.game.util.GameConfig;

public class Graphics {

	public static final int HCENTER = 1;
	public static final int VCENTER = 2;
	public static final int LEFT = 4;
	public static final int RIGHT = 8;
	public static final int TOP = 16;
	public static final int BOTTOM = 32;
	public static final int BASELINE = 64;

	public static final int TRANS_NONE = 0;
	public static final int TRANS_ROT90 = 5;
	public static final int TRANS_ROT180 = 3;
	public static final int TRANS_ROT270 = 6;
	public static final int TRANS_MIRROR = 2;
	public static final int TRANS_MIRROR_ROT90 = 7;
	public static final int TRANS_MIRROR_ROT180 = 1;
	public static final int TRANS_MIRROR_ROT270 = 4;
	
	public static final int COLOR_FILTER_NULL = 0xFFFFFFFF;

	private Canvas canvas;
	protected Paint paint;
	protected SparseArray<FontMetricsInt> fmiCache;
	protected SparseArray<ColorMatrixColorFilter> colorFilterCache;
	
	private Bitmap buffer;
	
	protected Graphics() {}
	
	public Graphics(int width, int height) {
		this(width, height, null);
	}
	
	public Graphics(int width, int height, Typeface font) {
		this(new Canvas());
		buffer = Bitmap.createBitmap(width, height, Config.ARGB_8888);
		buffer.setDensity(GameConfig.DENSITY);
		canvas.setBitmap(buffer);
		if (font != null) {
			paint.setTypeface(font);
		}
	}
	
	public Graphics(Canvas canvas) {
		this.canvas = canvas;
		canvas.setDensity(GameConfig.DENSITY);
		paint = new Paint();
		paint.setTextSize(GameConfig.FONT_DEFAULT_SIZE);
		fmiCache = new SparseArray<FontMetricsInt>();
		fmiCache.put(GameConfig.FONT_DEFAULT_SIZE, paint.getFontMetricsInt());
		canvas.save();
		colorFilterCache = new SparseArray<ColorMatrixColorFilter>();
	}
	
	public void setCanvas(Canvas canvas) {
		this.canvas = canvas;
	}
	
	public void setColor(int red, int green, int blue) {
		setColor(0xFF, red, green, blue);
	}
	
	public void setColor(int argb) {
		int a = argb >> 24 & 0xFF;
		int r = argb >> 16 & 0xFF;
		int g = argb >> 8 & 0xFF;
		int b = argb & 0xFF;
		if (a == 0) {
			a = 0xFF;
		}
		setColor(a, r, g, b);
	}
	
	public void setColor(int alpha, int red, int green, int blue) {
		if (alpha < 0 || alpha > 0xFF
				|| red < 0 || red > 0xFF
				|| green < 0 || green > 0xFF
				|| blue < 0 || blue > 0xFF) {
			throw new IllegalArgumentException("color out of range");
		}
		paint.setARGB(alpha, red, green, blue);
	}
	
	public int getClipX() {
    	return canvas.getClipBounds().left;
    }

    public int getClipY() {
    	return canvas.getClipBounds().top;
    }

    public int getClipWidth() {
    	return canvas.getClipBounds().width();
    }

    public int getClipHeight() {
    	return canvas.getClipBounds().height();
    }

    public void clipRect(int x, int y, int width, int height) {
    	canvas.clipRect(x, y, x + width, y + height);
    }

    public void setClip(int x, int y, int width, int height) {
    	canvas.restore();
    	canvas.save();
    	canvas.clipRect(x, y, x + width, y + height);
    }
    
    public Rect getClip() {
    	return canvas.getClipBounds();
    }

    public void drawLine(int x1, int y1, int x2, int y2) {
    	canvas.drawLine(x1, y1, x2, y2, paint);
    }

    public void fillRect(int x, int y, int width, int height) {
    	canvas.drawRect(x, y, x + width - 1, y + height - 1, paint);
    }
 
    public void drawRect(int x, int y, int width, int height) {
    	Style oldStyle = paint.getStyle();
    	paint.setStyle(Style.STROKE);
    	canvas.drawRect(x, y, x + width - 1, y + height - 1, paint);
    	paint.setStyle(oldStyle); // restore
    }

    public void drawRoundRect(int x, int y, int width, int height,
                                     int arcWidth, int arcHeight) {
    	Style oldStyle = paint.getStyle();
    	paint.setStyle(Style.STROKE);
    	canvas.drawRoundRect(new RectF(x, y, x + width - 1, y + height - 1), arcWidth, arcHeight, paint);
    	paint.setStyle(oldStyle);
    }
 
    public void fillRoundRect(int x, int y, int width, int height,
                                     int arcWidth, int arcHeight) {
    	canvas.drawRoundRect(new RectF(x, y, x + width, y + height), arcWidth, arcHeight, paint);
    }
                          
    public void fillArc(int x, int y, int width, int height,
                               int startAngle, int arcAngle) {
    	canvas.drawArc(new RectF(x, y, x + width, y + height), startAngle, arcAngle, false, paint);
    }

    public void drawArc(int x, int y, int width, int height,
                               int startAngle, int arcAngle) {
    	Style oldStyle = paint.getStyle();
    	paint.setStyle(Style.STROKE);
    	canvas.drawArc(new RectF(x, y, x + width, y + height), startAngle, arcAngle, false, paint);
    	paint.setStyle(oldStyle);
    }

    public void drawString(String str, int x, int y, int anchor) {
    	Align oldAlign = paint.getTextAlign();
    	int horizontal = anchor & (HCENTER | LEFT | RIGHT);
    	switch (horizontal) {
    		case HCENTER:
    			paint.setTextAlign(Align.CENTER);
    			break;
    		case LEFT:
    			paint.setTextAlign(Align.LEFT);
    			break;
    		case RIGHT:
    			paint.setTextAlign(Align.RIGHT);
    			break;
    	}
    	int vertical = anchor & (VCENTER | TOP | BOTTOM | BASELINE);
    	FontMetricsInt fmi = fmiCache.get(getTextSize());
    	if (fmi == null) {
    		fmi = paint.getFontMetricsInt();
    		fmiCache.put(getTextSize(), fmi);
    	}
    	switch (vertical) {
    		case VCENTER:
    			y += ((fmi.descent - fmi.ascent) >> 1) - fmi.descent;
    			break;
    		case TOP:
    			y -= fmi.ascent;
    			break;
    		case BOTTOM:
    			y -= fmi.descent;
    			break;
    		case BASELINE:
    			break;
    	}
    	canvas.drawText(str, x, y, paint);
    	paint.setTextAlign(oldAlign);
    }
    
    public void draw3DString(String str, int x, int y, int anchor, int textColor, int borderColor) {
    	setColor(borderColor);
    	drawString(str, x - 2, y, anchor); // left
    	drawString(str, x, y - 2, anchor); // top
    	drawString(str, x + 2, y, anchor); // right
    	drawString(str, x, y + 2, anchor); // bottom
    	setColor(textColor);
    	drawString(str, x, y, anchor);
    }

    public void drawSubstring(String str, int offset, int len,
                                     int x, int y, int anchor) {
    	drawString(str.substring(offset, offset + len), x, y, anchor);
    }

    public void drawChar(char character, int x, int y, int anchor) {
    	drawString(String.valueOf(character), x, y, anchor);
    }

    public void drawChars(char[] data, int offset, int length,
                                 int x, int y, int anchor) {
    	drawString(new String(data), x, y, anchor);
    }
 
    public void drawImage(Image image, int x, int y, int anchor) {
    	x = transX(image.getWidth(), x, anchor);
    	y = transY(image.getHeight(), y, anchor);
    	canvas.drawBitmap(image.getBitmap(), x, y, paint);
    }
    
    public void drawNinePatch(NinePatch np, Rect bound) {
    	np.draw(canvas, bound);
    }
    
    public void drawImage(Image image, Rect src, Rect dst) {
    	canvas.drawBitmap(image.getBitmap(), src, dst, paint);
    }
    
    public void drawImage(Image image, Matrix matrix) {
    	canvas.drawBitmap(image.getBitmap(), matrix, paint);
    }

    public void drawRegion(Image image, 
                                  int x_src, int y_src,
                                  int width, int height, 
                                  int transform,
                                  int x_dest, int y_dest, 
                                  int anchor) {
    	Bitmap dest = Bitmap.createBitmap(image.getBitmap(), x_src, y_src, width, height, transformMatrix(transform), true);
    	x_dest = transX(image.getWidth(), x_dest, anchor);
    	y_dest = transY(image.getHeight(), y_dest, anchor);
    	canvas.drawBitmap(dest, x_dest, y_dest, paint);
    	dest.recycle();
    }

    // use by draw image
    protected int transX(int imgWidth, int x, int anchor) {
    	int horizontal = anchor & (HCENTER | LEFT | RIGHT);
    	switch (horizontal) {
    		case HCENTER:
    			x -= imgWidth >> 1;
    			break;
    		case RIGHT:
    			x -= imgWidth;
    			break;
    	}
    	return x;
    }
    
    protected int transY(int imgHeight, int y, int anchor) {
    	int vertical = anchor & (VCENTER | TOP | BOTTOM | BASELINE);
    	switch (vertical) {
    		case VCENTER:
    			y -= imgHeight >> 1;
    			break;
    		case BOTTOM:
    			y -= imgHeight;
    			break;
    	}
    	return y;
    }

    protected Matrix transformMatrix(int transform) {
    	Matrix matrix = new Matrix();
    	switch (transform) {
    		case TRANS_NONE:
    			break;
    		case TRANS_ROT90:
    			matrix.setRotate(90);
    			break;
    		case TRANS_ROT180:
    			matrix.setRotate(180);
    			break;
    		case TRANS_ROT270:
    			matrix.setRotate(270);
    			break;
    		case TRANS_MIRROR:
    			matrix.setScale(-1, 1); // 翻转x
    			break;
    		case TRANS_MIRROR_ROT90:
    			matrix.setScale(-1, 1);
    			matrix.postRotate(90);
    			break;
    		case TRANS_MIRROR_ROT180:
    			matrix.setScale(-1, 1);
    			matrix.postRotate(180);
    			break;
    		case TRANS_MIRROR_ROT270:
    			matrix.setScale(-1, 1);
    			matrix.postRotate(270);
    			break;
    		default:
    			throw new IllegalArgumentException();
    	}
    	return matrix;
    }
    
    public void copyArea(int x_src, int y_src, int width, int height,
			 int x_dest, int y_dest, int anchor) {
    	throw new UnsupportedOperationException("copyArea");
    }

    public void fillTriangle(int x1, int y1, 
				    int x2, int y2,
				    int x3, int y3) {
    	Path path = new Path();
		path.moveTo(x1, y1);
		path.lineTo(x2, y2);
		path.lineTo(x3, y3);
		path.lineTo(x1, y1);
		path.close();
		canvas.drawPath(path, paint);
    }

    public void drawRGB(int[] rgbData, int offset, int scanlength,
			       int x, int y, int width, int height,
			       boolean processAlpha) {
    	canvas.drawBitmap(rgbData, offset, scanlength, x, y, width, height, processAlpha, paint);
    }
    
    public void drawBuffer(Canvas canvas, float x, float y, float scale) {
    	if (buffer == null) {
    		throw new NullPointerException("Graphics buffer null");
    	}
    	Matrix matrix = new Matrix();
    	matrix.setScale(scale, scale);
    	matrix.postTranslate(x, y);
    	canvas.drawBitmap(buffer, matrix, paint);
    }
    
    public void setTextSize(int size) {
    	paint.setTextSize(size);
    }
    
    public int getTextSize() {
    	return (int) paint.getTextSize();
    }
    
    public void setTypeface(Typeface typeface) {
    	paint.setTypeface(typeface);
    }

	public Typeface getTypeface() {
		return paint.getTypeface();
	}
	
	public void setColorFilter(int color) {
		if (color == COLOR_FILTER_NULL) {
			paint.setColorFilter(null);
			return;
		}
		float a = color >> 24 & 0xFF;
		float r = color >> 16 & 0xFF;
		float g = color >> 8 & 0xFF;
		float b = color & 0xFF;
		if (a == 0) {
			a = 0xFF;
		}
		ColorMatrixColorFilter filter = colorFilterCache.get(color);
		if (filter == null) {
			filter = new ColorMatrixColorFilter(new float[]{
					r / 0xFF, 0, 0, 0, 0,
					0, g / 0xFF, 0, 0, 0,
					0, 0, b / 0xFF, 0, 0,
					0, 0, 0, a / 0xFF, 0
			});
			colorFilterCache.put(color, filter);
		}
		paint.setColorFilter(filter);
	}
}
